using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using DownloadMailTask.POP3Client.Mime;
using ICSharpCode.SharpZipLib.Zip;
using Microsoft.SqlServer.Dts.Runtime;
using DownloadMailTask.POP3Client;

namespace DownloadMailTask
{
    public class MailDataReceiver
    {
        private POPClient _client;
        private readonly string _downloadPath;
        private readonly string _fileExtensions;
        private readonly string _serverName;
        private readonly int _port;
        private readonly string _account;
        private readonly string _password;
        private readonly bool _unzipAttachment;
        private readonly WriteOption _writeOption;
        private readonly bool _deleteMessage;
        private IDTSComponentEvents _events;
        private bool fireAgain = true;
        private readonly bool _saveEmailFile;

        public MailDataReceiver(IDTSComponentEvents componentEvents, POPClient client, string serverName, int port, string account, string password, bool deleteMessage, string fileExtensions, bool unzip, string downloadPath, WriteOption writeOption, bool saveEmailFile)
        {
            _events = componentEvents;
            _client = client;
            _downloadPath = downloadPath;
            _fileExtensions = fileExtensions;
            _serverName = serverName;
            _port = port;
            _account = account;
            _password = password;
            _unzipAttachment = unzip;
            _writeOption = writeOption;
            _deleteMessage = deleteMessage;
            _saveEmailFile = saveEmailFile;
        }

        public void Receive()
        {
            _client.Connect(_serverName, _port);
            _events.FireInformation(0, "", String.Format("Executing authentication to pop3 server {0}:{1}.",_serverName,_port), "", 0, ref fireAgain);
            _client.Authenticate(_account, _password);
            _events.FireInformation(0, "", "Autenthication succeeded.", "", 0, ref fireAgain);

            int i = _client.GetMessageCount();
            for (int j = 0; j < i; j++)
            {
                Message msg = _client.GetMessage(j + 1, false);

                if(msg != null)
                {
                    if (_saveEmailFile)
                        msg.SaveToMIMEEmailFile(
                            GetFileName(String.Concat(msg.Subject.Replace(' ', '_'), ".", msg.DateTimeInfo, ".eml")), false);

                    foreach(Attachment attachment in msg.Attachments)
                    {
                        if (attachment.ContentFileName != null && CheckFileExtension(attachment.ContentFileName))
                        {
                            if (_unzipAttachment && Path.GetExtension(attachment.ContentFileName) == ".zip")
                            {
                                using (MemoryStream zipFile = new MemoryStream(attachment.DecodedAttachment))
                                {
                                    Dictionary<string, MemoryStream> unzippedFiles = UnzipFile(zipFile);
                                    foreach (KeyValuePair<string, MemoryStream> current in unzippedFiles)
                                    {
                                        string fileName = current.Key;
                                        string destPath = GetFileName(fileName);
                                        using (FileStream fileStream = new FileStream(destPath, FileMode.CreateNew, FileAccess.Write))
                                        {
                                            current.Value.Position = 0;
                                            fileStream.Write(current.Value.GetBuffer(), 0, current.Value.Capacity);
                                            fileStream.Flush();
                                            _events.FireInformation(0, "", String.Format("Saving uzipped file attachment {0}.", destPath), "", 0, ref fireAgain);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                string destPath = GetFileName(attachment.ContentFileName);
                                msg.SaveAttachment(attachment, destPath);
                                _events.FireInformation(0, "", String.Format("Saving file attachment {0}.", destPath), "", 0, ref fireAgain);
                            }      
                        } 
                    }

                    if (_deleteMessage)
                        _client.DeleteMessage(j + 1);
                }
            }

            _client.Disconnect();
        }

        private bool CheckFileExtension(string fileExtension)
        {
            string[] validExtension = _fileExtensions.Split(';');
            for(int i = 0; i < validExtension.Length; i++)
            {
                if(Regex.Match(fileExtension, validExtension[i]).Success)
                    return true;
            }
            return false;
        }

        private string GetFileName(string fileName)
        {
            if (File.Exists(Path.Combine(_downloadPath,fileName)))
            {
                if (_writeOption == WriteOption.OverwriteExisting)
                    File.Delete(Path.Combine(_downloadPath, fileName));

                if (_writeOption == WriteOption.AutoNumber)
                {
                    string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);
                    string fileExtension = Path.GetExtension(fileName);

                    int c = 0;

                    while (true)
                    {
                        c++;
                        string newFileName = fileNameWithoutExtension + c + fileExtension;

                        if (!File.Exists(Path.Combine(_downloadPath, newFileName)))
                        {
                            fileName = newFileName;
                            break;
                        }
                    }
                }
            }

            return Path.Combine(_downloadPath, fileName);
        }

        private Dictionary<string, MemoryStream> UnzipFile(Stream zipfile)
        {
            Dictionary<string, MemoryStream> result = new Dictionary<string, MemoryStream>();

            using (ZipInputStream stream = new ZipInputStream(zipfile))
            {
                ZipEntry theEntry;
                while ((theEntry = stream.GetNextEntry()) != null)
                {
                    String filename = Path.GetFileName(theEntry.Name);

                    if (!String.IsNullOrEmpty(filename))
                    {
                        MemoryStream output = new MemoryStream();
                        Int32 size = 2048;
                        Byte[] data = new Byte[size];
                        while (true)
                        {
                            size = stream.Read(data, 0, data.Length);
                            if (size > 0) output.Write(data, 0, size);
                            else break;
                        }

                        result.Add(filename, output);
                    }
                }
            }

            return result;
        }
    }
}
